#pragma once

#include "Log.h"
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <sys/types.h>
#include <jsoncpp/json/json.h>

#define CONTENT_SEP " " // 用来分割正文内容的分隔符
#define CONTENT_SEP_LEN strlen(CONTENT_SEP) // 正文分隔符的长度
#define END_SEP "\r\n" // 用来标识结束的分隔符(包括报头和正文)
#define END_SEP_LEN strlen(END_SEP) // 结束分隔符的长度

constexpr int RECEIVE_BUFFER_SIZE = 1024;

enum {
    DIVIDE_ZERO = 1,
    MODE_ZERO,
    OPERATOR_ERROR
};

/**
 * 我们自己定制的协议，用来规范化客户端发送给服务端请求的格式
 * 方便服务端接收并处理请求
*/
struct Request {
    int _num1;
    int _num2;
    char _oper;

    Request()
        : _num1(0)
        , _num2(0)
        , _oper('\0')
    {}

    Request(const int& num1, const int& num2, const char& oper)
        : _num1(num1)
        , _num2(num2)
        , _oper(oper)
    {}

    /**
     * 对请求进行序列化
     * @param out 输出型参数，将序列化得到的字符串通过输出型参数返回回来
    */
    bool serialize(std::string* out) {
#ifdef JSON // 如果定义了宏JSON就采用json来序列化

        Json::Value root;

        // json是通过键值对来储存数据的
        // 类似于map或unordered_map

        //task 储存信息
        root["num1"] = _num1;
        root["num2"] = _num2;
        root["oper"] = _oper;

        //task 进行序列化
        Json::FastWriter writer; // 速度快，序列化出来的内容就一行
        // Json::StyleWrite write; // 写的好看，便于观察

        *out = writer.write(root); // 完成序列化，并通过输出型参数返回

#else // 否则就采用自己的序列化方案

        *out = ""; // 清空out内的内容

        *out += std::to_string(_num1);
        *out += CONTENT_SEP;
        *out += _oper; // string支持直接+= char类型
        *out += CONTENT_SEP;
        *out += std::to_string(_num2);

#endif // 结束条件编译

        return true;

    }//@end bool serialize(std::string* out)

    /**
     * 对传入的字符串进行反序列化
     * 并把成功反序列化得到的数据存储在当前request对象内
    */
    bool unserialize(const std::string& in) {
#ifdef JSON

        Json::Value root;
        Json::Reader reader;
        reader.parse(in, root); // 把in流中的数据反序列化，并把得到的数据存到root中

        _num1 = root["num1"].asInt();
        _num2 = root["num2"].asInt();
        _oper = static_cast<char>(root["oper"].asInt()); // 因为没有asChar方法，所以先用asInt得到对应字符的ASCII码，在隐式或显式的转成char类型

#else

        //* 自己手动序列化和反序列化是一个体力活!!!

        size_t left = in.find(CONTENT_SEP);
        size_t right = in.rfind(CONTENT_SEP);
        if (left == std::string::npos || right == std::string::npos ||
            left == right || right - (left + CONTENT_SEP_LEN) != 1) {

            logMessage(ERROR, "Request %s unserialize fail!", in.c_str());
            return false;
        }

        std::string num1_str = in.substr(0, left);
        std::string num2_str = in.substr(right + CONTENT_SEP_LEN);

        if (num1_str.empty() || num2_str.empty()) {
            logMessage(ERROR, "separate str err in Request unserialize %s %s", num1_str.c_str(), num2_str.c_str());
            return false;
        }

        _num1 = std::stoi(num1_str);
        _num2 = std::stoi(num2_str);
        _oper = in[left + CONTENT_SEP_LEN];

#endif

        return true;

    } //@end bool unserialize(const std::string& in)

}; //@end struct Request

/**
 * 规范化服务端发送给客户端回复的格式
*/
struct Response {
    int _exitcode;
    int _result;

    Response()
        : _exitcode(0)
        , _result(0)
    {}

    Response(const int& exitcode, const int& result)
        : _exitcode(exitcode)
        , _result(result)
    {}

    bool serialize(std::string* out) {
#ifdef JSON

        Json::Value root;
        root["exitcode"] = _exitcode;
        root["result"] = _result;

        Json::FastWriter writer;
        *out = writer.write(root);

#else

        *out = "";

        *out += std::to_string(_exitcode);
        *out += CONTENT_SEP;
        *out += std::to_string(_result);

#endif

        return true;

    } //@end bool serialize(std::string* out)

    bool unserialize(const std::string& in) {
#ifdef JSON

        Json::Value root;
        Json::Reader reader;
        reader.parse(in, root);

        _exitcode = root["exitcode"].asInt();
        _result = root["result"].asInt();

#else

    size_t mid = in.find(CONTENT_SEP);
    if (mid == std::string::npos) {
        logMessage(ERROR, "Response unserialize err! %s", in.c_str());
        return false;
    }

    std::string exitcode_str = in.substr(0, mid);
    std::string result_str = in.substr(mid + CONTENT_SEP_LEN);

    if (exitcode_str.empty() || result_str.empty()) {
        logMessage(ERROR, "separate str in Response unserialize %s %s", exitcode_str.c_str(), result_str.c_str());
        return false;
    }

    _exitcode = std::stoi(exitcode_str);
    _result = std::stoi(result_str);

#endif

        return true;

    } //@end bool unserialize(const std::string& in)

}; //@end struct Response

/**
 * 为已经完成序列化的Request和Response添加报头，为发送消息做准备
 * "num1 oper num2" -> "content_len(正文长度)"\r\n"num1 oper num2"\r\n
 * *当有多种报头时，可以在报头中带上报头的类型序号来标定是哪种报头
 * @param text 已经序列化完成的Request或Response
*/
std::string addHeader(const std::string& text) {
    std::string send_str(std::to_string(text.size()));
    send_str += END_SEP;
    send_str += text;
    send_str += END_SEP;

    return send_str;
}

/**
 * 为从网络中接收到的包文去掉报头
 * @param package 带有报头的数据包
 * @return 去掉报头后剩下的有效载荷
*/
std::string removeHeader(const std::string& package) {
    size_t pos = package.find(END_SEP);
    if (pos == std::string::npos) {
        logMessage(ERROR, "%s removeHeader fail!", package.c_str());
        return "";
    }

    int text_len = std::stoi(package.substr(0, pos));
    return std::string(package.substr(pos + END_SEP_LEN, text_len));
}

/**
 * 保证从字节流中读取到的是一个完整的Request或Response
 * *这里的Request和Response是带有报头的序列化数据
 * !这里要保证多进程或多线程是互斥的
 * @param sockfd 进行通信的套接字
 * @param package 输出型参数 将从字节流中读取到的完整信息通过text返回
*/
bool receivePackage(const int& sockfd, std::string* package) {
    // static保证了package_buffer的生命周期是随执行流的
    // 确保了即使当package_buffer内还有多组数据未读取时退出receivePackage
    // 下次进来时还能看见上次未读取到的数据，不会造成丢包
    static std::string package_buffer;
    char receive_buffer[RECEIVE_BUFFER_SIZE] = {};

    for (;;) {
        ssize_t n = recv(sockfd, receive_buffer, RECEIVE_BUFFER_SIZE - 1, 0);
        if (n > 0) { // 成功读取到了数据
            receive_buffer[n] = '\0';
            package_buffer += std::string(receive_buffer);

            size_t pos = package_buffer.find(END_SEP);
            if (pos == std::string::npos) {
                // 连报头都没读完，不用继续处理了，下一轮
                logMessage(NORMAL, "The complete package was not read %s", package_buffer.c_str());
                continue;
            }

            std::string text_len_str(package_buffer.substr(0, pos));
            int text_len = std::stoi(text_len_str); // 序列化后正文的长度
            int package_len = text_len_str.size() + (2 * END_SEP_LEN) + text_len; // 一整个完整包文的长度

            if (package_buffer.size() < package_len) {
                // 此时package_buffer中的数据量还没有一整个包文的数据量大
                // 那肯定没有一个完整的包文
                logMessage(NORMAL, "The complete package was not read %s", package_buffer.c_str());
                continue;
            }

            *package = package_buffer.substr(0, package_len);
            package_buffer.erase(0, package_len);

            return true;
        } //@end if (n > 0)
        else {
            logMessage(ERROR, "client quit or other errors in receivePackage");
            return false;
        }
        
    } //@end for (;;)
}